﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BattleThis.Core.Battle;

namespace BattleThis.Core.Battle
{
	public class BattleService
	{

		#region Variablen

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the game.
		/// </summary>
		/// <value>
		/// The game.
		/// </value>
		private Game Game { get; set; }

		private IEnumerable<IBattleInfluence> BattleInfulances { get; set; }

		#endregion

		#region Methoden

		/// <summary>
		/// Initializes a new instance of the <see cref="BattleService"/> class.
		/// </summary>
		/// <param name="map">The map.</param>
		public BattleService( Game game )
		{
			if( game == null ) throw new ArgumentNullException( "game" );

			this.Game = game;
			this.BattleInfulances = new IBattleInfluence[]{ new LandscapeInfluance(), 
															new ExperienceInfluence(), 
															new UnitTypeInfluance(), 
															new BalanceInfluance() };
		}

		/// <summary>
		/// Fight between the specified attacker.
		/// </summary>
		/// <param name="attacker">The attacker.</param>
		/// <param name="defender">The defener.</param>
		public void Fight( Unit attacker, Unit defender )
		{
			var context = new BattleContext( this.Game.Map, attacker, defender );
			var bonusDamage = this.BattleInfulances.Sum( x => x.Calculate( context ) );
			var damage = (int)( attacker.Weapon.Damage + bonusDamage );

			defender.Armor.Current -= damage < 1 ? 1 : damage;

			if( defender.Armor.IsDestroyed() )
			{
				context.DefencePosition.Unit = Unit.Empty;
				context.Attacker.Experience.Raise( 3 );

				var player = this.Game.HasUnit( defender );
				player.Army.Remove( defender );
			}
			else
			{
				context.Attacker.Experience.Raise( 2 );
				context.Defender.Experience.Raise( 1 );
			}
			context.Attacker.ActionPoints.Waste();
		}

		/// <summary>
		/// Get the Attack Range of a Unit.
		/// </summary>
		/// <param name="unit">The Unit to get the range.</param>
		/// <returns></returns>
		public IEnumerable<Position> GetRange( Unit unit, Player player )
		{
			if( unit == null ) throw new ArgumentNullException( "unit" );
			if( player == null ) throw new ArgumentNullException( "player" );

			if( unit.ActionPoints.IsWasted() ) return Enumerable.Empty<Position>();

			var position = this.Game.Map.Find( unit ).Position;
			var neighbours = this.Game.Map.GetNeighbours( position );

			return this.GetRange( neighbours, unit.Weapon.Range ).Except(
											new Position[]{position}).Distinct();

		}

		/// <summary>
		/// Gets the range.
		/// </summary>
		/// <param name="source">The source.</param>
		/// <param name="range">The range.</param>
		/// <returns></returns>
		private IEnumerable<Position> GetRange( IEnumerable<Position> source, int range )
		{
			if( source == null )throw new ArgumentNullException( "source" );

			range--;

			if( range <= 0 )
				return Enumerable.Empty<Position>();

			List<Position> result = new List<Position>();

			foreach( var item in source )
			{
				var neighbours = this.Game.Map.GetNeighbours( item );

				result.AddRange( neighbours );
				result.AddRange( this.GetRange( neighbours, range ) );
			}

			return result;
		}

	}
		#endregion
	
}